package com.csii.pe.dynamic.workflow;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.util.Map;
import com.csii.common.constants.SystemId;
import com.csii.ibs.Constants;
import com.csii.pe.common.util.BeanUtils;
import com.csii.pe.core.Context;
import com.csii.pe.core.PeException;
import com.csii.pe.core.PeRuntimeException;
import com.csii.pe.dynamic.jnl.JNLService;
import com.csii.pe.dynamic.jnl.JNLState;
import com.csii.pe.dynamic.service.CallbackService;
import com.csii.pe.dynamic.service.ProcessState;
import com.csii.pe.dynamic.service.RequestContext;
import com.csii.pe.dynamic.service.ResponseContext;
import com.csii.pe.dynamic.taskmodel.TaskModel;
import com.csii.pe.dynamic.taskmodel.TaskProcessType;
import com.csii.pe.dynamic.taskmodel.TaskState;
import com.csii.pe.dynamic.workflow.AccessControlPolicy;
import com.csii.pe.dynamic.workflow.CallbackServiceRegistry;
import com.csii.pe.dynamic.workflow.ExceptionHandler;
import com.csii.pe.dynamic.workflow.ReturnMessageResolver;
import com.csii.pe.dynamic.workflow.TransFailValidationRuntimeException;
import com.csii.pe.template.AbstractTemplate;
import com.csii.pe.transform.TransformException;
import com.csii.pe.transform.stream.extern.ExternUtil;
import com.csii.pe.validation.ValidationRuntimeException;


public class TaskProcessTemplate extends AbstractTemplate {

    private TaskModel taskModel;
    private CallbackServiceRegistry callbackServiceRegistry;
    private JNLService jnlService;
    private AccessControlPolicy accessControlPolicy;

    public void setAccessControlPolicy(AccessControlPolicy accessControlPolicy) {
        this.accessControlPolicy = accessControlPolicy;
    }

    public void setJnlService(JNLService jnlService) {
        this.jnlService = jnlService;
    }

    public void setCallbackServiceRegistry(CallbackServiceRegistry callbackServiceRegistry) {
        this.callbackServiceRegistry = callbackServiceRegistry;
    }

    public void setTaskModel(TaskModel taskModel) {
        this.taskModel = taskModel;
    }

    protected RequestContext initRequestContext(Context context) throws PeException {

        RequestContext requestContext = new RequestContext();

        requestContext.setRequestTransactionId(context.getTransactionId());
        Object mchTimestamp = context.getData("_MCHTimestamp");
        if (mchTimestamp != null) {
            if (mchTimestamp instanceof String)
                requestContext.setRequestMCHTimestamp(Timestamp.valueOf((String) mchTimestamp));
            else if (mchTimestamp instanceof java.util.Date) {
                if (mchTimestamp instanceof Timestamp)
                    requestContext.setRequestMCHTimestamp((Timestamp) mchTimestamp);
                else
                    requestContext.setRequestMCHTimestamp(new Timestamp(((java.util.Date) mchTimestamp).getTime()));
            } else {
                throw new PeException("pe.dms.workflow.invalid_request_header");
            }
        }
        if (context.getString("_MCHJnlNo") == null) {
            throw new ValidationRuntimeException("mca.channel_jnlno_empty");
        }
        requestContext.setRequestMChannelId(context.getString("_MChannelId"));
        requestContext.setRequestMCHJnlNo(context.getString("_MCHJnlNo"));
        requestContext.setRequestLoginType(context.getString("_LoginType"));
        requestContext.setRequestDeptSeq(context.getLong("_DeptSeq"));
        requestContext.setRequestCifSeq(context.getLong("_CifSeq"));
        requestContext.setRequestUserSeq(context.getLong("_UserSeq"));
        requestContext.setRequestProductId(context.getString("_ProductId"));
        requestContext.setRequestServiceId(context.getString("_ServiceId"));
        requestContext.setRequestTerminalId(context.getString("_TerminalId"));
        requestContext.setRequestTimestamp(context.getTimestamp());
        requestContext.setRequestToken(context.getString("TaskLock"));
        requestContext.setRequestDate(new java.sql.Date(context.getTimestamp().getTime()));
        return requestContext;
    }

    protected void beforeSubmit(RequestContext requestContext, Object request, Context context, ResponseContext responseContext) {

    }

    protected void finalHandle(RequestContext requestContext, Object request, Context context, ResponseContext responseContext) {

    }

    @SuppressWarnings("unchecked")
    public void execute(Context context) throws PeException {

        String RequestBean = (String) context.getString("RequestBean");

        Map m = (Map)ExternUtil.parse(RequestBean, Map.class.getClassLoader());
//        Map<String, Object> m2 = (Map<String, Object>) BeanUtils.bean2Map(m);
        context.setDataMap(m);

        // initRequestContext
        RequestContext requestContext = initRequestContext(context);

        Long taskSeq = context.getLong("TaskSeq");

        // 获取录入交易的请求上下文 , MCJNL
        RequestContext originRequestContext = jnlService.query(taskSeq);
        if (originRequestContext == null) {
            throw new PeRuntimeException("mca.task_not_found");
        }
        
        // 将 taskSeq 作为  _JnlNo 放入当前上下文内 
        context.setData(Constants.JNL_NO, taskSeq);
        
        // 获取svcId
        String svcId = originRequestContext.getRequestServiceId();

        CallbackService<?, ?> service = callbackServiceRegistry.getService(svcId);

        service.setRequestContext(requestContext);

        // 设置默认的返回上下文
        ResponseContext responseContext = new ResponseContext(SystemId.MCA.toString());
        service.setResponseContext(responseContext);
        // 恢复原录入交易信息
        Object request = service.restore(taskSeq);

        String taskType = context.getString("TaskProcessType");
        if (request == null && !(taskType != null && taskType.equals(TaskProcessType.RJ.name()))) {
            throw new PeException("mca.task_not_found");
        }

        Map dataMap = context.getDataMap();
        dataMap.put("_ParentJnlNo", taskSeq);

        try {
            if (accessControlPolicy != null) {
                try {
                    accessControlPolicy.permit(originRequestContext, request, context);
                } catch (Throwable t) {
                    responseContext = ExceptionHandler.handleException(context, t, responseContext.getResponseSystemId());
                    log.error(t.getMessage(), t);
                    return;
                }
            }

            // 写交易日志，并写个性化日志（调用回调服务的notify(ProcessState.I, request,requestContext) )
            Long jnlSeq = jnlService.write(requestContext, dataMap, null, null);
            context.setData("RequestJnlNo", jnlSeq);

            requestContext.setRequestJnlNo(jnlSeq);
            service.setRequestContext(requestContext); // set again for the case when the service
                                                       // copies requestContext instead of using
                                                       // reference.

            TaskProcessType taskProcessType = TaskProcessType.valueOf(taskType);
            // 使用jnlSeq作为任务号，若该服务需要授权，则授权模型在任务队列添加任务
            try {
                TaskState taskState = taskModel.processTask(taskProcessType, taskSeq, context.getString("Remark"), request, requestContext, originRequestContext);
                if (taskState != null) {
                    // 调用回调服务，更新当前的业务处理状态为待授权。（暂不考虑）
                	 Object response = ((CallbackService<Object, ?>) service).notify(taskState.getProcessState(), request, originRequestContext);
                	 // 将业务处理结果返回
                	 Map<?, ?> responseMap=null;
                	 if(response!=null){
 						responseMap= BeanUtils.bean2Map(response);
 					 }
	 				 String outObject = null;
 				 	 try {
 						outObject = ExternUtil.format(responseMap);
 					 } catch (TransformException e) {
 					 } catch (NoSuchAlgorithmException e) {
 					 	e.printStackTrace();
 					 } catch (IOException e) {
 						e.printStackTrace();
 					 }
 			        context.setData("ResponesBean", outObject);
 			          
                    // 交易还需再授权
                    // 修改当前授权交易日志状态
                    jnlService.updateState(jnlSeq, JNLState.C, responseContext, taskSeq, false);

                    if (taskState.getMessage() != null)
                        responseContext.setResponseRejMsg(taskState.getMessage());

                    return;
                }
            } catch (Throwable t) {
                log.error("fail_to_init_auth_task:" + jnlSeq, t);
                responseContext = ExceptionHandler.handleException(context, t, responseContext.getResponseSystemId());

                jnlService.updateState(jnlSeq, JNLState.F, responseContext, taskSeq, false);
                if (t instanceof TransFailValidationRuntimeException) {
                    // 在授权模型中,由于预约时间已过/预约时间格式错误会抛出此异常,交易状态设为失败
                    ((CallbackService<Object, ?>) service).notify(ProcessState.FL, request, originRequestContext);
                }
                return;
            }

            // Submit
            try {
                beforeSubmit(originRequestContext, request, context, responseContext);

                try {
                    // 个性业务的业务状态由callbackService自行完成
                    Object response = ((CallbackService<Object, ?>) service).notify(ProcessState.SB, request, originRequestContext);
                    if (response != null) {
                        Map responseMap = BeanUtils.bean2Map(response);
                        responseMap.put("_OriginTransactionId", originRequestContext.getRequestTransactionId());
                        context.setDataMap(responseMap);
                    }
                    // reget responseContext. Don't delete
                    // 注意:responseContext在service的调用过程中会被覆盖,重新取一次
                    responseContext = service.getResponseContext();
                    ReturnMessageResolver.resolveMessage(context, requestContext, responseContext);
                    JNLState jnlState = JNLState.resolveJNLState(responseContext);
                    jnlService.updateState(jnlSeq, jnlState, responseContext, taskSeq, true);
                } finally {
                    finalHandle(originRequestContext, request, context, responseContext);
                }
            } catch (Throwable t) {
                responseContext = ExceptionHandler.handleException(context, t, service.getResponseContext().getResponseSystemId());
                ((CallbackService<Object, ?>) service).notify(responseContext.getResponseProcessState(), request, originRequestContext);
                if (jnlSeq != null) {
                    JNLState jnlState = JNLState.resolveJNLState(responseContext);
                    jnlService.updateState(jnlSeq, jnlState, responseContext, taskSeq, true);
                }

                log.error("fail_to_submit_task:" + jnlSeq, t);
            } finally {
                if (taskModel != null) {
                    taskModel.notifyState(taskSeq, ProcessState.SB, responseContext.getResponseProcessState());
                }
            }
        } finally {
            service.setRequestContext(null);
            Map<?, ?> responseMap = BeanUtils.bean2Map(responseContext);
            context.setDataMap(responseMap);
            context.setData("_ProcessState", responseContext.getResponseProcessState());
        }
    }
}
